
import {mapObject} from "underscore";
import {rintToMultiple, toCardinalDegrees, length} from "./math.js";
import {ø} from "./objects.js";

// http://people.csail.mit.edu/jaffer/MIXF/MIXF-08
// https://www.iau.org/publications/proceedings_rules/units/

/**
 * For "a^x", surround the "x" with superscript tags: "a<sup>x</sup>"
 *
 * @param {string} s
 * @returns {string}
 */
export function markupForSymbol(s) {
    return s.replace(/\^([^\s./$)]*)/g, "<sup>$1</sup>");
}

/**
 * @param {number} val
 * @param units
 * @returns {string|undefined} the value formatted as a decimal number, or undefined if NaN
 */
function formatScalar(val, units) {
    return val === +val ? val.toFixed(units.precision) : undefined;
}

/**
 * Returns a human readable string for the provided vector in the given units. The vector has the parts [u, v].
 * See http://mst.nerc.ac.uk/wind_vect_convs.html.
 *
 * @param {number[]} vec2
 * @param units
 * @returns {string|undefined} the vector formatted as a readable string, or undefined if any vector component is NaN
 */
function formatVector(vec2, units) {
    const [u, v] = vec2;
    if (u === +u && v === +v) {
        vec2 = units.convention === "into" ? [-u, -v] : [u, v];  // invert direction by convention, if necessary
        const deg = rintToMultiple(toCardinalDegrees(vec2), 5);  // round to nearest 5 degrees on the compass rose
        return `${deg.toFixed(0)}° @ ${formatScalar(length(vec2), units)}`;
    }
}

/**
 * @param {number|number[]} val
 * @param units
 * @returns {string|undefined} the formatted value, or undefined if Nan
 */
function _format(val, units) {
    return Array.isArray(val) ? formatVector(val, units) : formatScalar(val, units);
}

function format(val, units) {
    const convert = v => v !== undefined ? units.convert(v) : undefined;
    const scalarize = v => v !== undefined ? units.scalarize(v) : undefined;
    const format = v => v !== undefined ? _format(v, units) : undefined;

    const formattedVal = format(convert(val));
    return {
        formattedVal,
        fullText: `${formattedVal ?? ""} ${units.symbol}`,
        scalarHTML: `${format(scalarize(convert(val))) ?? ""} ${units.html}`,
    };
}

/**
 * @param {string} symbol
 * @param {Object} [overrides]
 * @returns {{symbol: string, html: string}}
 */
export function createUnitDescriptor(symbol, overrides) {
    const base = {
        symbol,
        html: markupForSymbol(symbol),
        convert: x => x,        // number -> number or number[] -> number[], never undefined or null, but can be NaN
        scalarize: x => x,      // number -> number or number[] -> number, never undefined or null, but can be NaN
        precision: 0,           // number of decimal digits of precision
        convention: undefined,  // string or undefined
        format(val) { return format(val, this); },
    };
    return ø(base, overrides);
}

export function createUnitDescriptors(descriptors) {
    return mapObject(descriptors, (v, k) => createUnitDescriptor(k, v));
}
